"use strict";

var $ = require("jquery"),
    vizMocks = require("../../helpers/vizMocks.js"),
    mapLayerModule = require("viz/vector_map/map_layer");

QUnit.module("Basic", {
    beforeEach: function() {
        this.str = { layerType: "test-layer-type" };
        this.context = {
            renderer: new vizMocks.Renderer(),
            projection: { tag: "projection" },
            root: new vizMocks.Element(),
            name: "test-name",
            layer: { tag: "layer" },
            str: this.str,
            settings: { label: {} },
            grouping: {}
        };
        this.index = 10;
        this.coordinates = { tag: "coordinates" };
        this.attributes = { tag: "attributes" };
        this.element = new mapLayerModule._TESTS_MapLayerElement(this.context, this.index, { coordinates: this.coordinates }, this.attributes);
    },

    project: function(hasSeparateLabel) {
        var element = this.element;
        element.proj = { tag: "proj" };
        this.str.project = function() { return element.proj; };
        if(hasSeparateLabel) {
            element.labelProj = { tag: "label-proj" };
            this.context.hasSeparateLabel = true;
            this.str.projectLabel = function() { return element.labelProj; };
        }
        element.project();
    },

    draw: function(labelEnabled, hasSeparateLabel) {
        var element = this.element;
        this.project(hasSeparateLabel);
        this.str.draw = function() {
            element.figure = arguments[1];
            element.figure.root = new vizMocks.Element();
        };
        this.context.settings.label.enabled = labelEnabled;
        element.draw();
    }
});

QUnit.test("Project", function(assert) {
    this.str.project = sinon.spy();
    this.str.projectLabel = sinon.spy();

    this.element.project();

    assert.deepEqual(this.str.project.lastCall.args, [this.context.projection, this.coordinates], "project");
    assert.strictEqual(this.str.projectLabel.lastCall, null, "projectLabel");
});

QUnit.test("Project with labels", function(assert) {
    this.draw(true, true);
    this.str.getStyles = this.str.refresh = this.str.setState = this.str.transformLabel = $.noop;
    this.element.refresh();
    var proj = { tag: "proj" };
    this.str.project = sinon.stub().returns(proj);
    this.str.projectLabel = sinon.spy();
    this.context.hasSeparateLabel = true;

    this.element.project();

    assert.deepEqual(this.str.project.lastCall.args, [this.context.projection, this.coordinates], "project");
    assert.deepEqual(this.str.projectLabel.lastCall.args, [proj], "projectLabel");
});

QUnit.test("Project with separate labels without label", function(assert) {
    this.str.project = sinon.spy();
    this.str.projectLabel = sinon.spy();
    this.context.hasSeparateLabel = true;

    this.element.project();

    assert.deepEqual(this.str.project.lastCall.args, [this.context.projection, this.coordinates], "project");
    assert.strictEqual(this.str.projectLabel.lastCall, null, "projectLabel");
});

QUnit.test("Draw", function(assert) {
    var figure;
    this.str.draw = sinon.spy(function() {
        figure = arguments[1];
        figure.root = new vizMocks.Element();
    });

    this.element.draw();

    assert.strictEqual(this.str.draw.lastCall.args.length, 3, "draw");
    assert.strictEqual(this.str.draw.lastCall.args[0], this.context, "draw - context arg");
    assert.deepEqual(this.str.draw.lastCall.args[2], { index: this.index, name: "test-name" }, "draw - data arg");
    assert.deepEqual(figure.root.append.lastCall.args, [this.context.root], "root is appended");
    assert.strictEqual(this.context.renderer.stub("text").lastCall, null, "text is not created");
});

QUnit.test("Transform", function(assert) {
    this.draw();
    this.str.transform = sinon.spy();
    this.str.transformLabel = sinon.spy();

    this.element.transform();

    assert.deepEqual(this.str.transform.lastCall.args, [this.element.figure, this.context.projection, this.element.proj], "transform");
    assert.strictEqual(this.str.transformLabel.lastCall, null, "transformLabel");
});

QUnit.test("Transform with separate labels", function(assert) {
    this.draw(true, true);
    this.str.getStyles = this.str.refresh = this.str.setState = this.str.transformLabel = $.noop;
    this.str.projectLabel = sinon.stub().returns("label-proj");
    this.element.refresh();
    this.str.transform = sinon.spy();
    this.str.transformLabel = sinon.spy();

    this.element.transform();

    assert.deepEqual(this.str.transform.lastCall.args, [this.element.figure, this.context.projection, this.element.proj], "transform");
    assert.deepEqual(this.str.transformLabel.lastCall.args, [{
        root: this.element.figure.root, text: this.context.renderer.text.lastCall.returnValue, size: [0, 0], value: ""
    }, this.context.projection, "label-proj"], "transformLabel");
});

QUnit.test("Transform with separate labels without label", function(assert) {
    this.draw();
    this.str.transform = sinon.spy();
    this.str.transformLabel = sinon.spy();
    this.context.hasSeparateLabel = true;

    this.element.transform();

    assert.deepEqual(this.str.transform.lastCall.args, [this.element.figure, this.context.projection, this.element.proj], "transform");
    assert.strictEqual(this.str.transformLabel.lastCall, null, "transformLabel");
});

QUnit.test("Refresh", function(assert) {
    var styles = { tag: "styles" };
    this.draw();
    this.str.getStyles = sinon.stub().returns(styles);
    this.str.refresh = sinon.spy();
    this.str.setState = sinon.spy();

    this.element.refresh();

    assert.strictEqual(this.str.getStyles.lastCall.args.length, 1, "getStyles");
    var settings = this.str.getStyles.lastCall.args[0];
    assert.deepEqual(this.str.refresh.lastCall.args, [this.context, this.element.figure, { index: this.index, name: "test-name" }, this.element.proxy, settings], "refresh");
    assert.deepEqual(this.str.setState.lastCall.args, [this.element.figure, styles, 0], "setState");
});

QUnit.test("Refresh with label", function(assert) {
    var styles = { tag: "styles" };
    this.draw(true);
    this.str.getStyles = sinon.stub().returns(styles);
    this.str.refresh = sinon.spy();
    this.str.setState = sinon.spy();
    this.element.proxy.attribute("text", "Hello");
    $.extend(this.context.settings.label, {
        dataField: "text",
        stroke: "red", "stroke-width": 2, "stroke-opacity": 0.3,
        font: { size: 10, family: "fam" }
    });
    this.context.dataKey = "test-key";
    this.str.projectLabel = sinon.spy();
    this.str.transformLabel = sinon.spy();

    this.element.refresh();

    assert.strictEqual(this.str.getStyles.lastCall.args.length, 1, "getStyles");
    var settings = this.str.getStyles.lastCall.args[0],
        text = this.context.renderer.text.lastCall.returnValue;
    assert.deepEqual(this.str.refresh.lastCall.args, [this.context, this.element.figure, { index: this.index, name: "test-name" }, this.element.proxy, settings], "refresh");
    assert.deepEqual(this.str.setState.lastCall.args, [this.element.figure, styles, 0], "setState");
    assert.deepEqual(text.attr.getCall(1).args, [{ text: "Hello", x: 0, y: 0 }], "text settings 1");
    assert.deepEqual(text.attr.getCall(2).args, [{ align: "center", stroke: "red", "stroke-width": 2, "stroke-opacity": 0.3 }], "text settings 2");
    assert.deepEqual(text.css.lastCall.args, [{ "font-size": 10, "font-family": "fam" }], "text styles");
    assert.deepEqual(text.data.lastCall.args, ["test-key", { index: this.index, name: "test-name" }], "text data");
    assert.deepEqual(text.append.lastCall.args, [this.element.figure.root], "text is appended");
    assert.strictEqual(this.str.projectLabel.callCount, 0, "label is not projected");
    assert.strictEqual(this.str.transformLabel.callCount, 0, "label is not transformed");
});

QUnit.test("Refresh with label when labels are disabled", function(assert) {
    var styles = { tag: "styles" };
    this.draw(true);
    this.str.getStyles = sinon.stub().returns(styles);
    this.str.refresh = sinon.spy();
    this.str.setState = sinon.spy();
    this.element.proxy.attribute("text", "Hello");
    $.extend(this.context.settings.label, {
        dataField: "text",
        stroke: "red", "stroke-width": 2, "stroke-opacity": 0.3,
        font: { size: 10, family: "fam" }
    });
    this.context.dataKey = "test-key";
    this.str.projectLabel = sinon.spy();
    this.str.transformLabel = sinon.spy();
    this.element.refresh();
    var text = this.context.renderer.text.lastCall.returnValue;

    this.context.settings.label.enabled = false;
    this.element.refresh();

    assert.deepEqual(text.remove.lastCall.args, [], "text is removed");
});

QUnit.test("Refresh with label when labels are disabled and particular label is enabled", function(assert) {
    var styles = { tag: "styles" };
    this.draw(true);
    this.str.getStyles = sinon.stub().returns(styles);
    this.str.refresh = sinon.spy();
    this.str.setState = sinon.spy();
    this.element.proxy.attribute("text", "Hello");
    $.extend(this.context.settings.label, {
        enabled: false,
        dataField: "text",
        stroke: "red", "stroke-width": 2, "stroke-opacity": 0.3,
        font: { size: 10, family: "fam" }
    });
    this.context.dataKey = "test-key";
    this.element.update({ label: { enabled: true } });

    this.element.refresh();

    assert.strictEqual(this.context.renderer.stub("text").lastCall, null);
});

QUnit.test("Refresh with separate labels", function(assert) {
    this.context.labelRoot = new vizMocks.Element();
    this.draw(true, true);
    this.str.getStyles = this.str.refresh = this.str.setState = $.noop;
    this.element.proxy.attribute("text", "Hello");
    this.context.settings.label.dataField = "text";
    this.str.projectLabel = sinon.spy();
    this.str.transformLabel = sinon.spy();

    this.element.refresh();

    assert.deepEqual(this.context.renderer.text.lastCall.returnValue.append.lastCall.args, [this.context.labelRoot], "text is appended");
    assert.strictEqual(this.str.projectLabel.callCount, 1, "label is projected");
    assert.strictEqual(this.str.transformLabel.callCount, 1, "label is transformed");
});

QUnit.test("Refresh with label when text is in proxy", function(assert) {
    this.draw(true);
    this.str.getStyles = this.str.refresh = this.str.setState = $.noop;
    this.element.proxy.text = "Hello 2";

    this.element.refresh();

    assert.deepEqual(this.context.renderer.text.lastCall.returnValue.attr.getCall(1).args[0].text, "Hello 2", "text");
});

QUnit.test("Refresh with label when text is not defined", function(assert) {
    this.draw(true);
    this.str.getStyles = this.str.refresh = this.str.setState = $.noop;

    this.element.refresh();

    assert.strictEqual(this.context.renderer.text.lastCall.returnValue.stub("append").lastCall, null, "text is not appended");
});

QUnit.test("Options merging", function(assert) {
    var settings;
    this.str.getStyles = function() { settings = arguments[0]; };
    this.context.settings = {
        p1: "p1", p2: "p2",
        label: {
            p3: "p3", p4: "p4",
            font: {
                p5: "p5", p6: "p6"
            }
        }
    };
    this.element.update({
        p2: "P2", p3: "P3",
        label: {
            p4: "P4", p5: "p5",
            font: {
                p6: "P6", p7: "P7"
            }
        }
    });
    this.draw();
    this.str.refresh = this.str.setState = $.noop;

    this.element.refresh();

    assert.deepEqual(settings, {
        p1: "p1", p2: "P2", p3: "P3",
        label: {
            p3: "p3", p4: "P4", p5: "p5",
            font: {
                p5: "p5", p6: "P6", p7: "P7"
            }
        }
    });
});

QUnit.test("Options and palette", function(assert) {
    var settings;
    this.str.getStyles = function() { settings = arguments[0]; };
    this.context.settings = {
        _colors: ["a", "b", "c"],
        color: "Z",
        label: {}
    };
    this.element.update({
        paletteIndex: 2
    });
    this.draw();
    this.str.refresh = this.str.setState = $.noop;

    this.element.refresh();

    assert.strictEqual(settings.color, "c");
});

QUnit.test("Options and palette and option", function(assert) {
    var settings;
    this.str.getStyles = function() { settings = arguments[0]; };
    this.context.settings = {
        _colors: ["a", "b", "c"],
        color: "Z",
        label: {}
    };
    this.element.update({
        paletteIndex: 2,
        color: "d"
    });
    this.draw();
    this.str.refresh = this.str.setState = $.noop;

    this.element.refresh();

    assert.strictEqual(settings.color, "d");
});

QUnit.test("Options and grouping", function(assert) {
    var settings,
        callback1 = sinon.stub().withArgs("test-field-1").returns(3),
        callback2 = sinon.stub().withArgs("test-field-2").returns(5);
    this.str.getStyles = function() { settings = arguments[0]; };
    this.context.grouping = {
        prop1: {
            callback: callback1,
            field: "test-field-1",
            partition: [1, 2, 5],
            values: ["A", "B"]
        },
        prop2: {
            callback: callback2,
            field: "test-field-2",
            partition: [2, 6, 8],
            values: ["C", "D"]
        }
    };
    this.element.update();
    this.draw();
    this.str.refresh = this.str.setState = $.noop;

    this.element.refresh();

    assert.strictEqual(settings.prop1, "B");
    assert.strictEqual(settings.prop2, "C");
});

QUnit.test("measureLabel and adjustLabel", function(assert) {
    this.draw(true);
    this.str.getStyles = this.str.refresh = this.str.setState = $.noop;
    this.element.proxy.attribute("text", "Hello");
    this.context.settings.label.dataField = "text";
    this.element.refresh();
    var text = this.context.renderer.text.lastCall.returnValue;
    text.getBBox = sinon.stub().returns({ width: 20, height: 16, y: 3 });
    this.str.getLabelOffset = sinon.stub().returns([30, 40]);

    this.element.measureLabel();
    this.element.adjustLabel();

    assert.deepEqual(text.getBBox.lastCall.args, [], "measure");
    assert.deepEqual(text.attr.lastCall.args, [{ x: 30, y: 29 }], "adjust");
});

QUnit.test("Update when not drawn", function(assert) {
    this.element.update();

    assert.ok(true, "no error");
});

QUnit.test("Update when drawn", function(assert) {
    this.draw();
    this.str.getStyles = this.str.refresh = this.str.setState = $.noop;
    var refresh = sinon.spy(this.element, "refresh");

    this.element.update();

    assert.deepEqual(refresh.lastCall.args, []);
});

QUnit.test("Set hovered", function(assert) {
    var styles = { tag: "styles" },
        trigger = sinon.spy();
    this.context.params = { eventTrigger: trigger };
    this.context.hover = true;
    this.str.getStyles = function() { return styles; };
    this.str.refresh = this.str.setState = $.noop;
    this.str.setState = sinon.spy();
    this.draw();
    this.element.refresh();

    this.element.setHovered(true);

    assert.deepEqual(this.str.setState.lastCall.args, [this.element.figure, styles, 1], "setState");
    assert.deepEqual(this.element.figure.root.toForeground.lastCall.args, [], "toForeground");
    assert.deepEqual(trigger.lastCall.args, ["hoverChanged", { target: this.element.proxy, state: true }], "event");

    this.element.setHovered(false);

    assert.deepEqual(this.str.setState.lastCall.args, [this.element.figure, styles, 0], "setState");
    assert.deepEqual(this.element.figure.root.toBackground.lastCall.args, [], "toBackground");
    assert.deepEqual(trigger.lastCall.args, ["hoverChanged", { target: this.element.proxy, state: false }], "event");
});

QUnit.test("Set selected", function(assert) {
    var styles = { tag: "styles" },
        trigger = sinon.spy();
    this.context.params = { eventTrigger: trigger };
    this.context.selection = { state: {} };
    this.str.getStyles = function() { return styles; };
    this.str.refresh = this.str.setState = $.noop;
    this.str.setState = sinon.spy();
    this.draw();
    this.element.refresh();

    this.element.setSelected(true);

    assert.deepEqual(this.str.setState.lastCall.args, [this.element.figure, styles, 2], "setState");
    assert.deepEqual(this.element.figure.root.toForeground.lastCall.args, [], "toForeground");
    assert.deepEqual(trigger.lastCall.args, ["selectionChanged", { target: this.element.proxy, state: true }], "event");

    this.element.setSelected(false);

    assert.deepEqual(this.str.setState.lastCall.args, [this.element.figure, styles, 0], "setState");
    assert.deepEqual(this.element.figure.root.toBackground.lastCall.args, [], "toBackground");
    assert.deepEqual(trigger.lastCall.args, ["selectionChanged", { target: this.element.proxy, state: false }], "event");
});

QUnit.test("Set selected when not drawn", function(assert) {
    var trigger = sinon.spy();
    this.context.params = { eventTrigger: trigger };
    this.context.selection = { state: {} };

    this.element.setSelected(true);
    this.element.setSelected(false);

    assert.strictEqual(trigger.lastCall, null);
});

QUnit.test("Proxy fields", function(assert) {
    assert.strictEqual(this.element.proxy.layer, this.context.layer, "layer");
    assert.strictEqual(this.element.proxy.index, this.index, "index");
    assert.strictEqual(this.element.proxy.data, this.data, "data");
});

QUnit.test("Proxy.coordinates", function(assert) {
    assert.strictEqual(this.element.proxy.coordinates(), this.coordinates);
});

QUnit.test("Proxy.attribute", function(assert) {
    assert.strictEqual(this.element.proxy.attribute("prop1"), undefined, "getter");
    assert.strictEqual(this.element.proxy.attribute("prop1", "test-1"), this.element.proxy, "return value", "setter");
    assert.deepEqual(this.element.proxy.attribute(), { prop1: "test-1", tag: "attributes" }, "getter - all");
    assert.strictEqual(this.element.proxy.attribute("prop1"), "test-1", "getter");
});

QUnit.test("Proxy.selected getter", function(assert) {
    var stub = sinon.stub(this.element, "isSelected").returns("val");
    assert.strictEqual(this.element.proxy.selected(), "val");
    assert.deepEqual(stub.lastCall.args, []);
});

QUnit.test("Proxy.selected setter", function(assert) {
    var spy = sinon.spy(this.element, "setSelected");
    assert.strictEqual(this.element.proxy.selected("a", "b"), this.element.proxy);
    assert.deepEqual(spy.lastCall.args, ["a", "b"]);
});

QUnit.test("Proxy.applySettings", function(assert) {
    var spy = sinon.spy(this.element, "update");
    assert.strictEqual(this.element.proxy.applySettings({ tag: "settings" }), this.element.proxy);
    assert.deepEqual(spy.lastCall.args, [{ tag: "settings" }]);
});
